#include "NBuffer.h"

/*为了节省内存，一个字节掰成两半用*/
uint8 numbuf[6];
uint8 scibuf[2];
uint8 dotpot;

static void
n_buffer_clean (void)
{
        for (int i = 0; i < 6; ++i)
                numbuf[i] = 0x00;
        scibuf[0] = 0x0;
        scibuf[1] = 0x0;
        dotpot    = 0x0;
}

/**
 * 将 int32 类型转化为 Number Buffer
 */
static void
n_buffer_format_with_integer (void)
{
        int32 posi_num, tmp;
        uint8 is_positive;

        if (num1.number.l < 0) {
                posi_num    = -num1.number.l;
                is_positive = 0;
        } else {
                posi_num    = num1.number.l;
                is_positive = 1;
        }

        for (i = 0; i < 12; ++i) {
                tmp = posi_num % 10;
                numbuf[i / 2] |= (tmp << ((i % 2) * 4));
                posi_num -= tmp;
                posi_num /= 10;
                if (posi_num == 0 && !is_positive) {
                        if (i % 2)
                                numbuf[i / 2 + 1] = 0x0a;
                        else
                                numbuf[i / 2] |= 0xa0;
                        break;
                }
        }

        /*将空余的高位置 'd'*/
        for (i = 5; i > 0; --i) {
                if (numbuf[i] == 0x00) {
                        numbuf[i] = 0xdd;
                } else if ((numbuf[i] >> 4) == 0x0) {
                        numbuf[i] |= 0xd0;
                        break;
                } else {
                        break;
                }
        }
        if (!i && numbuf[i] == 0x00)
                numbuf[i] = 0xd0;
        if (!i && (numbuf[i] >> 4) == 0x0)
                numbuf[i] |= 0xd0;
}

/**
 * 是否是 (-1.0, 1.0) 之间的数
 */
#define IS_SMALLNUM(flags)    ((flags & 0x80) != 0)
#define SET_SMALLNUM(flags)   flags |= 0x80
#define UNSER_SMALLNUM(flags) flags &= 0x7f

/**
 * 是否是正数
 */
#define IS_POSITIVE(flags)    ((flags & 0x40) != 0)
#define SET_POSITIVE(flags)   flags |= 0x40
#define UNSER_POSITIVE(flags) flags &= 0xbf

/**
 * 是否是需要科学计数的数
 */
#define IS_SCINOTE(flags)    ((flags & 0x20) != 0)
#define SET_SCINOTE(flags)   flags |= 0x20
#define UNSER_SCINOTE(flags) flags &= 0xdf

/**
 * 将 浮点(float) 类型转化为 Number Buffer
 * 使用科学记数法的情况：
 *      1. 数字小于1
 *      2. 数字大于10^5
 * 其余情况均使用普通小数
 */
static void
n_buffer_format_with_float (void)
{
        float  cache;
        uint32 tmp;
        uint8  flags = 0x20, grade, nflag = 11;
        if (num1.number.f >= 0)
                SET_POSITIVE (flags);

        cache = (num1.number.f >= 0 ? num1.number.f : -num1.number.f);
        /*若不需要科学记数法，则跳过指数装载*/
        if ((cache >= 1.0 && cache < 1e5) || cache == 0.0) {
                UNSER_SCINOTE (flags);
                goto step2_load_digit;
        }
        /*步骤一：装载指数*/
        if (cache < 1.0) {
                cache = (1.0 / cache) * 10.0;
                SET_SMALLNUM (flags);
        }

        for (tmp = 0; cache > 10.0; ++tmp)
                cache /= 10.0;
        grade = (uint8)tmp;

        if (IS_SMALLNUM (flags)) {
                if (tmp < 10) {
                        scibuf[1] = 0xbc;
                        scibuf[0] = 0xa0;
                        scibuf[0] |= tmp;
                } else {
                        scibuf[1] = 0xca;
                        scibuf[0] = tmp % 10;
                        tmp       = ((tmp - tmp % 10) / 10) << 4;
                        scibuf[0] |= tmp;
                }
        } else if (tmp < 10) {
                scibuf[1] = 0xbd;
                scibuf[0] = 0xc0;
                scibuf[0] |= tmp;
        } else {
                scibuf[1] = 0xbc;
                scibuf[0] = tmp % 10;
                tmp       = ((tmp - tmp % 10) / 10) << 4;
                scibuf[0] |= tmp;
        }
        /*化普通浮点，如 0.006655 -> 6.655e-3，取 6.655 */
        cache = (num1.number.f >= 0 ? num1.number.f : -num1.number.f);
        for (i = 0; i < grade; ++i)
                if (IS_SMALLNUM (flags))
                        cache *= 10.0;
                else
                        cache /= 10.0;

step2_load_digit:
        /*步骤二：装载小数点位置*/
        dotpot = 0xb0;
        /*科学记数法小数点位置为 1 或者 2*/
        if (IS_SCINOTE (flags)) {
                if (IS_POSITIVE (flags))
                        dotpot |= 0x01;
                else
                        dotpot |= 0x02;
                goto step3_load_decimal;
        }
        for (grade = 1; cache > 10.0; ++grade)
                cache /= 10.0;
        if (!IS_POSITIVE (flags))
                grade += 1;
        dotpot |= grade;
        /*还原浮点数*/
        cache = (num1.number.f >= 0 ? num1.number.f : -num1.number.f);

step3_load_decimal:
        /*步骤三：装载浮点数字*/
#define LOAD_HALF_BIT(ib) numbuf[nflag / 2] |= (nflag % 2 ? (ib) << 4 : (ib))

        if (!IS_POSITIVE (flags)) {
                numbuf[nflag / 2] |= 0xa0;
                nflag -= 1;
        }
        tmp = (uint32)cache;
        /*保留小数部分*/
        cache -= (float)tmp;

        /*载入整数部分*/
        for (uint32 i = 100000; i > 0; i /= 10) {
                if (tmp < i)
                        continue;
                for (; i > 0; i /= 10) {
                        grade = (uint8)(tmp / i);
                        tmp -= i * grade;
                        LOAD_HALF_BIT (grade);
                        nflag -= 1;
                }
        }
        cache *= 1e9;
        tmp = (uint32)cache;
        /*载入小数部分*/
        for (uint32 i = 100000000; i > 1; i /= 10) {
                grade = (uint8)(tmp / i);
                tmp -= i * grade;
                LOAD_HALF_BIT (grade);
                if (!nflag)
                        break;
                nflag -= 1;
        }
}

/**
 * 将 Number 类转化为数码管预缓存 Number Buffer
 */
void
n_buffer_format (void)
{
        /*清除现有缓存*/
        n_buffer_clean ();

        if (num1.type == N_TYPE_INTEGER)
                n_buffer_format_with_integer ();
        else
                n_buffer_format_with_float ();
}

uint8
n_buffer_getnum_at (uint8 index)
{
        if (index > 11)
                return 0;
        if (index % 2) {
                index = numbuf[index / 2];
                index &= 0xf0;
                index >>= 4;
        } else {
                index = numbuf[index / 2];
                index &= 0x0f;
        }
        return index;
}